'use strict';

// Data Model for Lamp plant

/**
 * Module dependences
 */

const _         = require('lodash');
const Promise   = require('bluebird');
const path      = require('path');
const Sequelize = require('sequelize');
const config    = require(path.join(__dirname, 'config.json'));
const utils     = require(path.join(__dirname, '..', 'helpers', 'utils.js'));

const dbFile      = path.join(__dirname, '..', '..', 'data', 'sansi.db3');
const MAC_PADDING = '0000000000000000';

/**
 * Init the db connection
 */
var sequelize = new Sequelize('plant_lamp', 'sansi', 'plantlamp', {
  dialect: 'sqlite',
  pool: {
    max: config.pool.max,
    min: config.pool.max,
    idle: 10000
  },
  storage: dbFile,
  define: {
    timestamps: false
  },
  logging: false
});

/**
 * Controller table
 */
var Controller = sequelize.define('controller', {
  id: {type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true, allowNull: false},
  name: {type: Sequelize.STRING(128), allowNull: false },
  devMac: {
    type: Sequelize.STRING(16), 
    field:'mac', 
    allowNull: false,
    set: function(val) {
      this.setDataValue('devMac', utils.pad(MAC_PADDING, val, true).toUpperCase());
    }
  },
  ip_addr: {type: Sequelize.STRING(16)},
  ip_port: {type: Sequelize.INTEGER},
  note: {type: Sequelize.STRING(100), field: 'note'}  
}, {  
  tableName: 'controller',
  indexes: [{unique: true, fields: ['mac']}]
});

var ControllerStatus = sequelize.define('controller_status', {
  controller_id: {
    type: Sequelize.INTEGER, 
    allowNull: false,
    references: { model: Controller, key: 'id' }
  },
  comm_status: {type: Sequelize.INTEGER, defaultValue: 0},
  status: {type: Sequelize.INTEGER, defaultValue: 255},
  work_mode: {type: Sequelize.INTEGER, defaultValue: 255},
  type: {type: Sequelize.INTEGER, defaultValue: 1},
  v_software: {type: Sequelize.STRING(12), allowNull: true, defaultValue: null},
  v_system: {type: Sequelize.STRING(12), allowNull: true, defaultValue: null},
  v_kernel: {type: Sequelize.STRING(12), allowNull: true, defaultValue: null},
  v_hardware: {type: Sequelize.STRING(12), allowNull: true, defaultValue: null},
  manufacturer: {type: Sequelize.STRING(100), defaultValue: ''}
}, {
  tableName: 'controller_status',
  indexes: [{unique: true, fields: ['controller_id']}]
});

Controller.hasOne(ControllerStatus, {foreignKey: 'controller_id', onDelete: 'cascade', hooks: true});
ControllerStatus.belongsTo(Controller, {foreignKey: 'controller_id'});

/**
 * Controller hooks
 */
Controller.hook('afterCreate', (obj, options)=> {
  return ControllerStatus.create({controller_id: obj.id});
});

Controller.hook('afterBulkCreate', (instances, options)=> {
  let mac_list = _.map(instances, (o)=> {return o.getDataValue('mac');});
  console.log(mac_list);
  Controller.findAll({
    raw: true,
    where: {devMac: {$in: mac_list}},
    attributes: ['id']
  }).then((objs)=> {
    let new_objs = _.map(objs, (o)=> {return {controller_id: o.id}});
    ControllerStatus.bulkCreate(new_objs);
  });
});

/**
 * Device table
 */
var Device = sequelize.define('device',{
  id: {type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true, allowNull: false},
  name: {type: Sequelize.STRING(128), allowNull: false },
  devMac: {
    type: Sequelize.STRING(16), 
    field: 'mac', 
    allowNull: false,
    set: function(val) {
      this.setDataValue('devMac', utils.pad(MAC_PADDING, val, true).toUpperCase());
    }
  },
  note: {type: Sequelize.STRING(100), field: 'note'},
}, {
  tableName: 'device',
  indexes: [{unique: true, fields: ['mac']}]
});

// readonly in current system
var DeviceStatus = sequelize.define('device_status', {
  device_id: {
    type: Sequelize.INTEGER, 
    allowNull: false,
    references: { model: Device, key: 'id' }
  },
  v_software: {type: Sequelize.INTEGER, defaultValue: null},
  manufacturer: {type: Sequelize.STRING(250), defaultValue: ''},
  type: {type: Sequelize.INTEGER, defaultValue: 255},
  volt: {type: Sequelize.DOUBLE, defaultValue: 0},
  amp: {type: Sequelize.DOUBLE, defaultValue: 0},
  power: {type: Sequelize.DOUBLE, defaultValue: 0},
  uptime: {type: Sequelize.INTEGER, defaultValue: 0},
  lighting_time: {type: Sequelize.INTEGER, defaultValue: 0},
  e_consumption: {type: Sequelize.INTEGER, defaultValue: 0},
  transition_duration: {type: Sequelize.INTEGER, defaultValue: 0},
  coefficiency: {type: Sequelize.INTEGER, defaultValue: 100},
  brightness: {type: Sequelize.INTEGER, defaultValue: 0},
  brightness_min: {type: Sequelize.INTEGER, defaultValue: 0},
  brightness_max: {type: Sequelize.INTEGER, defaultValue: 100},
  brightness_fault: {type: Sequelize.INTEGER, defaultValue: 0},
  brightness_power_on: {type: Sequelize.INTEGER, defaultValue: 100},
  switch: {type: Sequelize.INTEGER, defaultValue: 255},
  status: {type: Sequelize.INTEGER, defaultValue: 255},
  comm_status: {type: Sequelize.INTEGER, defaultValue: 255},
  lamp_status: {type: Sequelize.INTEGER, defaultValue: 255}
}, {
  tableName: 'device_status',
  indexes: [{unique: true, fields: ['device_id']}]
});

Device.hasOne(DeviceStatus, {foreignKey: 'device_id', onDelete: 'cascade', hooks: true});
DeviceStatus.belongsTo(Device, {foreignKey: 'device_id'});

/**
 * Controller hooks
 */
Device.hook('afterCreate', (obj, options)=> {
  return DeviceStatus.create({device_id: obj.id});
});

Device.hook('afterBulkCreate', (instances, options)=> {
  let mac_list = _.map(instances, (o)=> {return o.getDataValue('mac');});
  Device.findAll({
    raw: true,
    where: {devMac: {$in: mac_list}},
    attributes: ['id']
  }).then((objs)=> {
    let new_objs = _.map(objs, (o)=> {return {device_id: o.id}});
    DeviceStatus.bulkCreate(new_objs);
  });
});

/**
 * Frontend Controller Device table
 */
var ControllerDevices = sequelize.define('controller_devices', {
  device_id: {
    type: Sequelize.INTEGER, 
    primaryKey: true, 
    allowNull: false,
    references: { model: Device, key: 'id' }
  },
  controller_id: {
    type: Sequelize.INTEGER, 
    primaryKey: true, 
    allowNull: false,
    references: { model: Controller, key: 'id' }
  },
}, {
  tableName: 'controller_devices'
});

Controller.hasMany(ControllerDevices, {foreignKey: 'controller_id', onDelete: 'cascade', hooks: true});
ControllerDevices.belongsTo(Controller, {foreignKey: 'controller_id'});

Device.hasOne(ControllerDevices, {foreignKey: 'device_id', onDelete: 'cascade', hooks: true});
ControllerDevices.belongsTo(Device, {foreignKey: 'device_id'});

/**
 * Frontend Group table
 */
var Group = sequelize.define('group', {
  id: {type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true, allowNull: false},
  group_mask: {type: Sequelize.INTEGER, allowNull: false},
  name: {type: Sequelize.STRING(64), allowNull: false }
}, {
  tableName: 'frontend_group'
});

/**
 * Frontend Controller Group table
 */
var ControllerGroups = sequelize.define('controller_groups', {
  controller_id: {
    type: Sequelize.INTEGER, 
    primaryKey: true, 
    allowNull: false,
    references: { model: Controller, key: 'id' }
  },
  group_id: {
    type: Sequelize.INTEGER, 
    primaryKey: true, 
    allowNull: false,
    references: { model: Group, key: 'id' }
  },
}, {
  tableName: 'frontend_cotnroller_groups'
});

Controller.hasMany(ControllerGroups, {foreignKey: 'controller_id', onDelete: 'cascade', hooks: true});
ControllerGroups.belongsTo(Controller, {foreignKey: 'controller_id'});

Group.hasOne(ControllerGroups, {foreignKey: 'group_id', onDelete: 'cascade', hooks: true});
ControllerGroups.belongsTo(Group, {foreignKey: 'group_id'});

/**
 * Frontend Group Device table
 */
var GroupDevices = sequelize.define('group_devices', {
  device_id: {
    type: Sequelize.INTEGER, 
    primaryKey: true, 
    allowNull: false,
    references: { model: Device, key: 'id' }
  },
  group_id: {
    type: Sequelize.INTEGER, 
    primaryKey: true, 
    allowNull: false,
    references: { model: Group, key: 'id' }
  },
}, {
  tableName: 'frontend_group_devices'
});

Group.hasMany(GroupDevices, {foreignKey: 'group_id', onDelete: 'cascade', hooks: true});
GroupDevices.belongsTo(Group, {foreignKey: 'group_id'});

Device.hasOne(GroupDevices, {foreignKey: 'device_id', onDelete: 'cascade', hooks: true});
GroupDevices.belongsTo(Device, {foreignKey: 'device_id'});

/**
 * Frontend Greenhouse Group table
 */
var Greenhouse = sequelize.define('greenhouse', {
  id: {type: Sequelize.INTEGER, primaryKey: true, allowNull: false},
  name: {type: Sequelize.STRING(64), allowNull: false }  
}, {
  tableName: 'greenhouse'
});

/**
 * Frontend Greenhouse Group table
 */
var GreenhouseGroups = sequelize.define('greenhouse_groups', {
  house_id: {
    type: Sequelize.INTEGER, 
    primaryKey: true, 
    allowNull: false,
    references: { model: Greenhouse, key: 'id' }
  },
  group_id: {
    type: Sequelize.INTEGER, 
    primaryKey: true, 
    allowNull: false,
    references: { model: Group, key: 'id' }
  },
}, {
  tableName: 'greenhouse_groups'
});

Greenhouse.hasMany(GreenhouseGroups, {foreignKey: 'house_id', onDelete: 'cascade', hooks: true});
GreenhouseGroups.belongsTo(Greenhouse, {foreignKey: 'house_id'});

Group.hasOne(GreenhouseGroups, {foreignKey: 'group_id', onDelete: 'cascade', hooks: true});
GreenhouseGroups.belongsTo(Group, {foreignKey: 'group_id'});

/**
 * Register the export tables.
 */
var db = {};
db.Name              = 'plant_lamp';
db.Controller        = Controller;
db.ControllerStatus  = ControllerStatus;
db.Device            = Device;
db.DeviceStatus      = DeviceStatus;
db.ControllerDevices = ControllerDevices;
db.Group             = Group;
db.ControllerGroups  = ControllerGroups;
db.GroupDevices      = GroupDevices;
db.Greenhouse        = Greenhouse;
db.GreenhouseGroups  = GreenhouseGroups;

/**
 * Exports
 */

db.sequelize = sequelize;
db.Sequelize = Sequelize;

module.exports = db;

if (!module.parent) {
  Controller.bulkCreate([{
    name: 'ctrl_01',
    devMac: 'FF01',
    'ip_addr': '127.0.0.1',
    'ip_port': '3434'
  }, {
    name: 'ctrl_02',
    devMac: 'FF02',
    'ip_addr': '127.0.0.1',
    'ip_port': '3434'
  }]).then(()=> {
    console.log('done');
  });
  // Controller.destroy({where: {id: 1}})
  // .then((ret)=> {
  //   console.log(ret);
  // });
}